﻿//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//
//    Crolengi, this program (a part of program) is gameengine based on C++\Directx
//    Copyright (C) 2008 Pashinin Anton Alekseevich (Crol)
//
//    This program is free software: you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.
//    (INCLUDING NEGLIGENCE OR OTHERWISE) 
//    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 
//    EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//    Contacts: 
//    Mail: crolengi@gmail.com
//
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define physx_main_h

// мои инклуды :)
#include "..\\import\\CRLEngi_strings_lib\\CRLEngi_strings.h"
#include "..\\import\\param_trees_lib\\param_trees.h"
#include "include\\Utils\\list.h"
#include "include\\Utils\\Console.h"

// дефайны для подключения VRD физикса
#define SAMPLES_USE_VRD 0
	//Change this setting to the IP number or DNS name of the computer that is running the VRD
#define SAMPLES_VRD_HOST "localhost"
	//Change this setting to the port on which the VRD is listening, or keep the default: NX_DBG_DEFAULT_PORT
#define SAMPLES_VRD_PORT NX_DBG_DEFAULT_PORT
	//Change this setting to decide what type of information is sent to the VRD. Default: NX_DBG_EVENTMASK_EVERYTHING
#define SAMPLES_VRD_EVENTMASK NX_DBG_EVENTMASK_EVERYTHING

// вектор понадобился для временного хранения списка вершин
#include <vector>
using namespace std;




// инклуды библиотек физикса
#include "include//physx//NxPhysics.h"
#include "include//physx//cooking.h"
#include "include//physx/nxUtilities.h"
#include "include//physx//Stream.h"
#include "include//physx//SamplesVRDSettings.h"
#include "include//physx//Controller.h"
#include "include//physx//CharacterControllerManager.h"
#include "include//physx//Stream.h"
//#include "include//physx//NxPhysics.h"
#include "include//physx//cooking.h"
#include "include//physx//NxPhysicsSDK.h"
#include "include//physx//NxPMap.h"
#include "include//physx//PhysXLoader.h"
#include "include//physx//cooking_stream.h"

#include "include//physx//ErrorStream.h"

#pragma comment(lib, "include//physxloader.lib")
#pragma comment(lib, "include//nxcooking.lib")
#pragma comment(lib, "include//NxCharacter.lib")

class UserAllocator;

#include "include//physx//UserAllocator.h" ;
#include "include//physx//UserAllocator.cpp" ;

#include "include//CC//character.h" ;

class    CLREngi_PhysX {
public:
 NxPhysicsSDK  *g_pPhysicsSDK;
 NxScene       *g_pScene;

 UserAllocator* gMyAlloc;

 NxControllerManager* ccMgr;
 //MyContactReport* CC;

 int		gAppData;

 NxVec3        g_vGravity;
 ErrorStream   g_errorStream;

 CLREngi_Console        *Console;
protected:	
//-----------------------------------------------------------------------------------------
// подключалка дебагера
//-----------------------------------------------------------------------------------------
   void vrd_init(){
	
	 g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->connect(SAMPLES_VRD_HOST, SAMPLES_VRD_PORT, SAMPLES_VRD_EVENTMASK);
	 if (g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->isConnected()){

		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->createObject(&gAppData, NX_DBG_OBJECTTYPE_GENERIC, "AppData", NX_DBG_EVENTMASK_EVERYTHING);
		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->writeParameter("Whatever data would be interesting for debugging", &gAppData, true, "Info", NX_DBG_EVENTMASK_EVERYTHING);
		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->createObject(&gAppData+1, NX_DBG_OBJECTTYPE_VECTOR, "AVector", NX_DBG_EVENTMASK_EVERYTHING);
		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->writeParameter(NxVec3(0, 0, 0), &gAppData+1, true, "Origin", NX_DBG_EVENTMASK_EVERYTHING);
		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->writeParameter(NxVec3(1, 1, 1), &gAppData+1, true, "Extent", NX_DBG_EVENTMASK_EVERYTHING);
		g_pPhysicsSDK->getFoundationSDK().getRemoteDebugger()->addChild(&gAppData, &gAppData+1, NX_DBG_EVENTMASK_EVERYTHING);
		gAppData = 1; //Tells our application that we have created the VRD object and that it is ok to set information in it
	 }
   }

public:
//-----------------------------------------------------------------------------------------
// [функция получения вершин с директх модельки]
//-----------------------------------------------------------------------------------------
   vector<float> GetVertices(LPD3DXMESH    *g_pMesh ){
     
      vector<float> vertices;
      DWORD stride =  D3DXGetFVFVertexSize((*g_pMesh)->GetFVF());
      BYTE* vbptr = NULL;
      
	  (*g_pMesh)->LockVertexBuffer(0, (LPVOID*)&vbptr);
 
	  int ii = -1;
      for(int i = 0; i < (*g_pMesh)->GetNumVertices(); i++){
        
		ii++;
        D3DXVECTOR3* pos = (D3DXVECTOR3*)vbptr;
        vertices.push_back(pos->x);
        vertices.push_back(pos->y);
        vertices.push_back(pos->z);
        
        vbptr += stride;
      }
      (*g_pMesh)->UnlockVertexBuffer();
      //delete vbptr;

    return vertices;
   }
//-----------------------------------------------------------------------------------------
// функция получения индексов с директх модельки]
//-----------------------------------------------------------------------------------------
   vector<short> GetIndices(LPD3DXMESH     *g_pMesh){

    LPVOID * ppData;
    DWORD stride = sizeof(short);
    BYTE* ibptr = NULL;

    short* indices = new short[(*g_pMesh)->GetNumFaces() * 3];

    vector<short> copy;

    (*g_pMesh)->LockIndexBuffer(0, (LPVOID*)&indices);

    for(int i = 0; i < (*g_pMesh)->GetNumFaces() * 3; i++){
        copy.push_back(indices[i]);
    }

    (*g_pMesh)->UnlockIndexBuffer();

    // delete indices;
    return copy;
   }

//-----------------------------------------------------------------------------------------
//  Два варианта разработки функций генерации статичных и динамичных мешей, 
//  в первом варианте мы разделяем генерацию на две функции, что удобно для отлова ошибок
//
// Две функции для генерации конвекса
//-----------------------------------------------------------------------------------------
NxConvexMeshDesc GenConvexDesc( vector<float> vertices, const NxReal density)
{
    int i = 0;
    NxActorDesc actorDesc;
    NxBodyDesc bodyDesc;

	int numVertices = vertices.size() / 3;

    NxVec3* verts = new NxVec3[numVertices];

    int ii = -1;
    for(int i = 0; i < numVertices; i++)
    {
        ++ii;
        verts[i].x = vertices[ii];
        verts[i].y = vertices[++ii];
        verts[i].z = vertices[++ii];
    }

    // Create descriptor for convex mesh
    NxConvexMeshDesc convexDesc;
    convexDesc.numVertices            = numVertices;
    convexDesc.pointStrideBytes        = sizeof(NxVec3);
    convexDesc.points                = verts;

    convexDesc.flags                = NX_CF_COMPUTE_CONVEX;

    return convexDesc;
}


NxActor* GenConvexFromDesc(const NxVec3& pos,NxConvexMeshDesc& desc, bool stat)
{
    NxActorDesc actorDesc;

    NxInitCooking();
    MemoryWriteBuffer buf;
    bool status = NxCookConvexMesh(desc, buf);

    NxConvexShapeDesc* convexShapeDesc    =    new NxConvexShapeDesc();
    convexShapeDesc->localPose.t          =     NxVec3(0, 0, 0);
    convexShapeDesc->materialIndex        =    0;

    convexShapeDesc->meshData = g_pPhysicsSDK->createConvexMesh(MemoryReadBuffer(buf.data));


	convexShapeDesc->group = GROUP_COLLIDABLE_NON_PUSHABLE;

    actorDesc.shapes.pushBack(convexShapeDesc);


    NxBodyDesc bodyDesc;

	bodyDesc.angularDamping	= 0.5f;

	//bodyDesc.linearVelocity = NxVec3(10000.0f,-1000.0f,0.0f);

    if(!stat)
    {
        actorDesc.body = &bodyDesc;
        actorDesc.density = 1;
    }
    else
        actorDesc.body = NULL;

    actorDesc.globalPose.t  = pos;
    actorDesc.name = "boo";
    actorDesc.density		= 1.0f;

	actorDesc.group = GROUP_COLLIDABLE_NON_PUSHABLE;

    if(!actorDesc.isValid())
        NULL;

    NxActor* actor = g_pScene->createActor(actorDesc);

    return actor;
}
//-----------------------------------------------------------------------------------------
// Теперь создание одной функцией статичного физического объекта
//-----------------------------------------------------------------------------------------
NxActor* GenTriangleMesh(NxVec3 pos, vector<short> indices, vector<float> vertices )
{
   int NumVerticies = vertices.size() / 3;
    int NumTriangles = indices.size() / 3;
    //Create pointer for vertices
    NxVec3* verts = new NxVec3[NumVerticies];

	int ii = -1;
    for(int i = 0; i < NumVerticies; i++)
    {
        ++ii;
        verts[i].x = vertices[ii];
        verts[i].y = vertices[++ii];
        verts[i].z = vertices[++ii];
    }
    //Create pointer for indices
    NxU16 *tris = new NxU16[indices.size()];
    for(int i = indices.size() - 1; i >= 0; i--)
        tris[i] = i;

    // Build physical model
    NxTriangleMeshDesc TriMeshDesc;
    TriMeshDesc.numVertices = NumVerticies;
    TriMeshDesc.numTriangles = NumTriangles;
    TriMeshDesc.pointStrideBytes = sizeof(NxVec3);
    TriMeshDesc.triangleStrideBytes = 3*sizeof(NxU16);
    TriMeshDesc.points = verts;
    TriMeshDesc.triangles = tris;
	//TriMeshDesc.group = GROUP_COLLIDABLE_NON_PUSHABLE;
    TriMeshDesc.flags = NX_MF_16_BIT_INDICES ;//| NX_MF_FLIPNORMALS  ;
    
    NxTriangleMeshShapeDesc ShapeDesc;
    NxInitCooking();

    // Cooking from memory
    MemoryWriteBuffer buf;
    bool status = NxCookTriangleMesh(TriMeshDesc, buf);
    ShapeDesc.meshData = g_pPhysicsSDK->createTriangleMesh(MemoryReadBuffer(buf.data));

    NxActorDesc actorDesc;
    actorDesc.shapes.pushBack(&ShapeDesc);
    actorDesc.globalPose.t = pos;

	actorDesc.group = GROUP_COLLIDABLE_NON_PUSHABLE;
	

    NxActor* act = g_pScene->createActor(actorDesc);

    delete[] verts;
    delete[] tris;

    return act; 
}
//-----------------------------------------------------------------------------------------------
// прототип функции для создания сонвекса, сейчас он не мультифункциональна,
// потому что используется конкретный физ. объект
//-----------------------------------------------------------------------------------------------
NxActor* MakeConvexFromX(LPD3DXMESH* pMesh, bool StaticModel , float x,float y,float z){
NxActor* objecto;
  	objecto = GenConvexFromDesc( NxVec3(x,y,z), GenConvexDesc(GetVertices(pMesh),10.0f) ,false);
//if (object)
   return objecto;
//else return false;
}
//-----------------------------------------------------------------------------------------------
// прототип функции для создания статичного объекта, сейчас он не мультифункциональна,
// потому что используется конкретный физ. объект
//-----------------------------------------------------------------------------------------------
NxActor* MakeTriMeshFromX( LPD3DXMESH* pMesh, bool StaticModel , float x,float y,float z){
NxActor* objecto;
	objecto =  GenTriangleMesh(NxVec3(x,y,z),GetIndices(pMesh),GetVertices(pMesh)) ;
//if (object)
	
   return objecto;
//else return false;
}



void Simulate(NxReal FPSMSeconds){
    //g_pScene->simulate( FPSMSeconds/60.0f );
	g_pScene->simulate( FPSMSeconds  );
}
void SimulationEnd(){
    g_pScene->flushStream();
    g_pScene->fetchResults( NX_RIGID_BODY_FINISHED, true );

}


//-----------------------------------------------------------------------------
// Инициализация физикса
//-----------------------------------------------------------------------------
void InitPhysX( void )
{
	gMyAlloc=new UserAllocator;

	int ErrorsCount = 0 ;
    // Initialize the PhysX Physics SDK
	

    g_pPhysicsSDK = NxCreatePhysicsSDK( NX_PHYSICS_SDK_VERSION, 0, &g_errorStream );

	if( g_pPhysicsSDK ){

		ccMgr=NxCreateControllerManager(gMyAlloc);

		g_pPhysicsSDK->setParameter(NX_SKIN_WIDTH, 0.025f);

	    g_pPhysicsSDK->setParameter(NX_VISUALIZATION_SCALE, 1.0f);
	    g_pPhysicsSDK->setParameter(NX_VISUALIZE_COLLISION_SHAPES, 1);
        

		// Create a scene
		NxSceneDesc sceneDesc;
		sceneDesc.gravity              = g_vGravity;

		g_pScene = g_pPhysicsSDK->createScene( sceneDesc );

		NxReal myTimestep = 1.0f/60.0f;  

	  // g_pScene->setTiming(myTimestep / 4.0f, 4, NX_TIMESTEP_FIXED);

	    g_pScene->setTiming(myTimestep ,1, NX_TIMESTEP_FIXED);


		NxMaterial *defaultMaterial = g_pScene->getMaterialFromIndex(0); 
		defaultMaterial->setRestitution(0.2f);
		defaultMaterial->setStaticFriction(0.45f);
		defaultMaterial->setDynamicFriction(0.45f);

		// Create a ground plane for our cubes to collide with.
		NxPlaneShapeDesc PlaneDesc;
		NxActorDesc ActorDesc;
		ActorDesc.shapes.pushBack( &PlaneDesc );
		ActorDesc.globalPose.t = NxVec3(0,-1000,0);
		g_pScene->createActor(ActorDesc);
		
        Console->AddText("[+] Объект PlaneDesc в 0,0,0");

		 vrd_init();
	}else{
	  ErrorsCount++;
      Console->AddText("[!] Не удалось инициализировать PhysXSDK, вероятно отсутствуют драйвера.",1);
	}
	    CRLEngi_string message;
		message = "[!] PhysX Manager setup completed with ";
		message += (int)ErrorsCount;
		message += " errors.";
		//message += (int)LOADED_objects;
		//message += " objects.";
		
		if(ErrorsCount)
		    Console->AddText(message,1); 
		else
			Console->AddText(message,3); 
	
}

void ReleasePhysX( void )
{
    if( g_pPhysicsSDK && g_pScene )   
        g_pPhysicsSDK->releaseScene( *g_pScene );

    g_pPhysicsSDK->release();
}

//-----------------------------------------------------------------------------------------------
// конструктор
//-----------------------------------------------------------------------------------------------
public:
  CLREngi_PhysX(CLREngi_Console   *Cnsl){
     g_pPhysicsSDK = NULL;
     g_pScene = NULL;

     g_vGravity = NxVec3( 0.0f, -1400.81f, 0.0f );

	 Console = Cnsl;
  
  };
};

#endif